#include "liodebug.h"

namespace Lio {

#ifdef _DEBUG_SRC
	long __src_index = -1,__src_cursor = 0,__src_line[SRC_LENGTH] = {0};
	const char *__src_name[SRC_LENGTH] = {0},*__src_file[SRC_LENGTH] = {0};
#endif

#ifdef _DEBUG_MEM
	static long
		__alloc_memory=0,
		__alloc_count=0;
	static unsigned long
		__alloc_search_total=0,
		__alloc_count_add=0,
		__alloc_search_count=0,
		__alloc_search_max=0,
		__alloc_search_avg=0;
	static long
		__alloc_max_memory=0,
		__alloc_max_count=0;
	typedef struct __mem_struct {
		const char *ptr,*func,*file;
		long mem,line;
	} __mem_info;
	static __mem_info __mem_infos[MAX_ALLOC_COUNT]={0};

	void __mem_add(const void *buff, unsigned long mem, CALL_PARAM)
	{
		if(!buff)return;
		if(__alloc_count>=MAX_ALLOC_COUNT)
		{
			assert(FALSE);
			return;
		}
		long alloc_index=__alloc_count++;
		__mem_info *info=__mem_infos+alloc_index;
		assert(!info->ptr);
		if(__alloc_max_count<__alloc_count)
			__alloc_max_count=__alloc_count;
		__alloc_memory+=mem;
		if(__alloc_max_memory<__alloc_memory)
			__alloc_max_memory=__alloc_memory;
		info->ptr=(char*)buff;
		info->mem=mem;
		info->func=func;
		info->file=file;
		info->line=line;
		__alloc_count_add++;
	}
	void __mem_rem(const void *buff, CALL_PARAM)
	{
		if(!buff)return;
		if(!__alloc_count)
		{
			assert(FALSE);
			return;
		}
		unsigned long search;
		__mem_info *rem_info=NULL;
		for(long i=__alloc_count-1;i>=0;i--)
		{
			if(__mem_infos[i].ptr==(char*)buff)
			{
				rem_info=__mem_infos+i;
				search=__alloc_count-i-1;
				break;
			}
		}
		if(!rem_info)
		{
			assert(FALSE);
			return;
		}
		__alloc_count--;
		__alloc_memory-=rem_info->mem;
		if(search)
		{
			if(__alloc_search_max<search)
				__alloc_search_max=search;
			__alloc_search_avg=(__alloc_search_total+=search)/(++__alloc_search_count);
			__mem_info *alloc_info=__mem_infos+__alloc_count;
			assert(alloc_info->ptr);
			*rem_info=*alloc_info;
			rem_info=alloc_info;
		}
		rem_info->ptr=NULL;
	}
	void __mem_dump(BOOL verbose)
	{
		PRINT(_T("\nMemory: now %dB (%dKB), max %dB (%dKB)\n"),__alloc_memory,__alloc_memory>>10,__alloc_max_memory,__alloc_max_memory>>10);
		PRINT(_T("Allocs: now %d (%d%%), max %d (%d%%), total %d\n"),__alloc_count,100*__alloc_count/MAX_ALLOC_COUNT,__alloc_max_count,100*__alloc_max_count/MAX_ALLOC_COUNT, __alloc_count_add);
		PRINT(_T("Search: max %d, avg %d\n"),__alloc_search_max,__alloc_search_avg);
		unsigned long total_mem=0,total_allocs=0,i;
		__mem_info *info;
		for(i=0,info=__mem_infos;i<MAX_ALLOC_COUNT;i++,info++)
		{
			if(!info->ptr)continue;
			total_mem+=info->mem;
			total_allocs++;
			if(verbose)PRINT("\nalloc[%d]=%d\nfunc=%s(%d): %s\n", i, info->mem, info->file, info->line, info->func);
		}
		PRINT("Tested: %dB (%dKB) total memory by %d allocs\n\n", total_mem, total_mem>>10, total_allocs);
		FLUSH_DEBUG_OUT();
	}
#endif

BOOL __debugger_present=IsDebuggerPresent();

#ifdef _DEBUG_PRINT
	FILE *__debug_out=NULL,*__prev_debug_out=NULL;
	TempDebugOutput::TempDebugOutput(FILE *file)
	{
		handle=file;
		SET_DEBUG_OUT(handle);
	}
	TempDebugOutput::TempDebugOutput(CSTR name, CSTR mode)
	{
		if(!file.open(name,mode))return;
		handle=file;
		SET_DEBUG_OUT(handle);
	}
	TempDebugOutput::~TempDebugOutput()
	{
		REM_DEBUG_OUT(handle);
	}
#endif

#ifdef _DEBUG_TIME
	static PrecisionTimer __t[MAX_TIMES];
	static float sTime[MAX_TIMES]={};
	static DWORD nTime[MAX_TIMES]={};
	static float aTime[MAX_TIMES]={};
	static float mTime[MAX_TIMES]={};
	void start_monitor_time(int profile)
	{
		assert(profile<MAX_TIMES);
		if(profile<MAX_TIMES)
		{
			__t[profile].restart();
		}
	}
	void stop_monitor_time(int profile)
	{
		assert(profile<MAX_TIMES);
		if(profile<MAX_TIMES)
		{
			float x=__t[profile].restart();
			sTime[profile]+=x;
			nTime[profile]++;
			aTime[profile]=sTime[profile]/nTime[profile];
			if(x>mTime[profile])
				mTime[profile]=x;
		}
	}
	void print_monitor_times()
	{
		for(int i=0; i<MAX_TIMES; i++)
			PRINT(_T("Timer %d: avg = %f ms, max = %f ms\n"), 1000*aTime[i], 1000*mTime[i]);
	}
#endif //_DEBUG_TIME

static const char *GetExceptionName(unsigned int code)
{
	switch(code)
	{
		case EXCEPTION_ACCESS_VIOLATION:		return "The thread tried to read from or write to a virtual address for which it does not have the appropriate access.";
		case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:	return "The thread tried to access an array element that is out of bounds and the underlying hardware supports bounds checking.";
		case EXCEPTION_BREAKPOINT:				return "A breakpoint was encountered.";
		case EXCEPTION_DATATYPE_MISALIGNMENT:	return "The thread tried to read or write data that is misaligned on hardware that does not provide alignment.";
		case EXCEPTION_FLT_DENORMAL_OPERAND:	return "One of the operands in a floating-point operation is denormal. A denormal value is one that is too small to represent as a standard floating-point value.";
		case EXCEPTION_FLT_DIVIDE_BY_ZERO:		return "The thread tried to divide a floating-point value by a floating-point divisor of zero.";
		case EXCEPTION_FLT_INEXACT_RESULT:		return "The result of a floating-point operation cannot be represented exactly as a decimal fraction.";
		case EXCEPTION_FLT_INVALID_OPERATION:	return "This exception represents any floating-point exception not included in this list.";
		case EXCEPTION_FLT_OVERFLOW:			return "The exponent of a floating-point operation is greater than the magnitude allowed by the corresponding type.";
		case EXCEPTION_FLT_STACK_CHECK:			return "The stack overflowed or underflowed as the result of a floating-point operation.";
		case EXCEPTION_FLT_UNDERFLOW:			return "The exponent of a floating-point operation is less than the magnitude allowed by the corresponding type.";
		case EXCEPTION_ILLEGAL_INSTRUCTION:		return "The thread tried to execute an invalid instruction.";
		case EXCEPTION_IN_PAGE_ERROR:			return "The thread tried to access a page that was not present, and the system was unable to load the page. ";
		case EXCEPTION_INT_DIVIDE_BY_ZERO:		return "The thread tried to divide an integer value by an integer divisor of zero.";
		case EXCEPTION_INT_OVERFLOW:			return "The result of an integer operation caused a carry out of the most significant bit of the result.";
		case EXCEPTION_INVALID_DISPOSITION:		return "An exception handler returned an invalid disposition to the exception dispatcher.";
		case EXCEPTION_NONCONTINUABLE_EXCEPTION:return "The thread tried to continue execution after a noncontinuable exception occurred.";
		case EXCEPTION_PRIV_INSTRUCTION:		return "The thread tried to execute an instruction whose operation is not allowed in the current machine mode.";
		case EXCEPTION_SINGLE_STEP:				return "A trace trap or other single-instruction mechanism signaled that one instruction has been executed.";
		case EXCEPTION_STACK_OVERFLOW:			return "The thread used up its stack.";
	}
	return "Unknown exception";
}
int __exception(struct _EXCEPTION_POINTERS *pEP)
{
	PRINT("\nEXCEPTION!\n");
	PRINT("--------------------------------------------------\n");

	PEXCEPTION_RECORD exc = pEP->ExceptionRecord;
	PCONTEXT cont = pEP->ContextRecord;
	unsigned int code = exc->ExceptionCode;

	char CrashModulePathName[MAX_PATH];
	char *CrashModuleFileName = "Unknown";

	MEMORY_BASIC_INFORMATION MemInfo;
	if (VirtualQuery((void*)cont->Eip, &MemInfo, sizeof(MemInfo)))
	if(GetModuleFileName((HINSTANCE)MemInfo.AllocationBase, CrashModulePathName, MAX_PATH)>0)
		CrashModuleFileName = strrchr(CrashModulePathName, '\\') + 1;
	PRINT("Module:        \t%s\r\n", CrashModuleFileName);
	PRINT("Address:       \t%04x:%08x\r\n", cont->SegCs, cont->Eip);
	PRINT("Error:         \t%s\r\n", GetExceptionName(code));
	PRINT("Remarks:       \t");
	switch (exc->ExceptionCode)
	{
	case EXCEPTION_ACCESS_VIOLATION: 
		if (exc->NumberParameters >= 2) 
		{
			const char* readwrite = "Read from";
			if(exc->ExceptionInformation[0])
				readwrite = "Write to";
			PRINT("%s location %08x\r\n", readwrite, exc->ExceptionInformation[1]);
			break;
		} 
	default: PRINT("none\r\n");
	}
	PRINT("--------------------------------------------------\n");
	return __debugger_present ? EXCEPTION_CONTINUE_SEARCH : EXCEPTION_EXECUTE_HANDLER;
}

}; // namespace Lio